﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

namespace SXICE.Core.Helpers
{
    /// <summary>
    /// 安全帮助类
    /// </summary>
    public class SecurityHelper
    {
        /// <summary>
        /// des向量
        /// </summary>
        private static readonly byte[] desIV = { 0x72, 0x86, 0x63, 0x56, 0x88, 0xAB, 0xCD, 0xAB };
        /// <summary>
        /// AES向量
        /// </summary>
        private static readonly string aesIV = "vmy#79VK^P10t%4y";
        private static readonly string aesKey = "0^!Nzk*e1h0|hkji$bP5S,xNx7?x9xQJ&";

        /// <summary>
        /// DES加密字符串
        /// </summary>
        /// <param name="encryptString">待加密的字符串</param>
        /// <param name="encryptKey">加密密钥,要求为8位</param>
        /// <returns>加密成功返回加密后的字符串，失败返回源串</returns>
        public static string EncryptDES(string encryptString, string encryptKey)
        {
            try
            {
                encryptKey = (encryptKey ?? "").PadRight(8, 'a').Substring(0, 8);
                var rgbKey = Encoding.UTF8.GetBytes(encryptKey);
                var rgbIV = desIV;
                var inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                var dCSP = new DESCryptoServiceProvider();
                using (var mStream = new MemoryStream())
                using (var cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write))
                {
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    return Convert.ToBase64String(mStream.ToArray());
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// DES解密字符串
        /// </summary>
        /// <param name="decryptString">待解密的字符串</param>
        /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>
        /// <returns>解密成功返回解密后的字符串，失败返源串</returns>
        public static string DecryptDES(string decryptString, string decryptKey)
        {
            try
            {
                decryptKey = (decryptKey ?? "").PadRight(8, 'a').Substring(0, 8);
                var rgbKey = Encoding.UTF8.GetBytes(decryptKey);
                var rgbIV = desIV;
                var inputByteArray = Convert.FromBase64String(decryptString);
                using (var dcsp = new DESCryptoServiceProvider())
                using (var mStream = new MemoryStream())
                using (var cStream = new CryptoStream(mStream, dcsp.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write))
                {
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    return Encoding.UTF8.GetString(mStream.ToArray());
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="decryptString">AES密文</param>
        /// <param name="key">秘钥（44个字符）</param>
        /// <returns>解密结果</returns>
        public static string AESDecrypt(string decryptString, string key = null)
        {
            if (decryptString.IsNullOrEmpty())
                return decryptString;
            key = key ?? aesKey;
            key = key.PadRight(32, ' ').Substring(0, 32);
            var aes = new RijndaelManaged();
            var iv = Encoding.UTF8.GetBytes(aesIV);
            aes.Key = Encoding.UTF8.GetBytes(key.Substring(0, 32));
            aes.Mode = CipherMode.ECB;
            aes.IV = iv;
            aes.Padding = PaddingMode.PKCS7;
            var rijndaelDecrypt = aes.CreateDecryptor();
            var inputData = Convert.FromBase64String(decryptString);
            var xBuff = rijndaelDecrypt.TransformFinalBlock(inputData, 0, inputData.Length);
            return Encoding.UTF8.GetString(xBuff);
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="encriyptString">要被加密的字符串</param>
        /// <param name="key">秘钥（44个字符）</param>
        /// <returns>加密结果</returns>
        public static string AESEncrypt(string encriyptString, string key = null)
        {
            if (encriyptString.IsNullOrEmpty())
                return encriyptString;
            key = key ?? aesKey;
            key = key.PadRight(32, ' ').Substring(0, 32);
            var aes = new RijndaelManaged();
            var iv = Encoding.UTF8.GetBytes(aesIV);
            aes.Key = Encoding.UTF8.GetBytes(key.Substring(0, 32));
            aes.Mode = CipherMode.ECB;
            aes.IV = iv;
            aes.Padding = PaddingMode.PKCS7;
            ICryptoTransform rijndaelEncrypt = aes.CreateEncryptor();
            var inputData = Encoding.UTF8.GetBytes(encriyptString);
            var encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length);
            return Convert.ToBase64String(encryptedData);
        }

        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="content">要加密的内容</param>
        /// <returns>加密后的内容</returns>
        public static string MD5Encryption(string content)
        {
            if (content.IsNullOrEmpty())
                return content;
            using (var md5 = new MD5CryptoServiceProvider())
            {
                var bytes = Encoding.UTF8.GetBytes(content);
                var result = BitConverter.ToString(md5.ComputeHash(bytes));
                return result.Replace("-", "");
            }
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="content">要加密的内容</param>
        /// <param name="publickey">公钥</param>
        /// <returns></returns>
        public static string RSAEncrypt(string content, string publickey)
        {
            var rsa = new RSACryptoServiceProvider();
            byte[] cipherbytes;
            rsa.FromXmlStringExtensions(publickey);
            cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(content), false);
            return Convert.ToBase64String(cipherbytes);
        }

        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="content">要解密的内容</param>
        /// <param name="privatekey">私钥</param>
        /// <returns></returns>
        public static string RSADecrypt(string content, string privatekey)
        {
            var rsa = new RSACryptoServiceProvider();
            byte[] cipherbytes;
            rsa.FromXmlStringExtensions(privatekey);
            cipherbytes = rsa.Decrypt(Convert.FromBase64String(content), false);
            return Encoding.UTF8.GetString(cipherbytes);
        }
    }

    /// <summary>
    /// System.Security.Cryptography.RSA 扩展方法
    /// </summary>
    internal static class RSAExtensions
    {
        // 处理 下面两种方式都会出现的 Operation is not supported on this platform 异常
        // RSA.Create().FromXmlString(privateKey) 
        // new RSACryptoServiceProvider().FromXmlString(privateKey) 

        /// <summary>
        /// 扩展FromXmlString
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="xmlString"></param>
        public static void FromXmlStringExtensions(this RSA rsa, string xmlString)
        {
            RSAParameters parameters = new RSAParameters();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);

            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
            {
                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    }
                }
            }
            else
            {
                throw new Exception("Invalid XML RSA key.");
            }

            rsa.ImportParameters(parameters);
        }

        /// <summary>
        /// 扩展ToXmlString
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="includePrivateParameters"></param>
        /// <returns></returns>
        public static string ToXmlStringExtensions(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                  parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                  parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                  parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                  parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                  parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                  parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                  parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                  parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
        }
    }
}